package com.demo.function.caffeine;

import com.github.benmanes.caffeine.cache.AsyncLoadingCache;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * Caffeine功能测试
 * @author cyj
 */
@Slf4j
public class CaffeineService {

    private final AtomicInteger atomicInteger = new AtomicInteger();

    private final Cache<String, String> cache = Caffeine.newBuilder()
            .expireAfterWrite(1,TimeUnit.MINUTES)
            .maximumSize(10_000)
            .removalListener((key, value, cause) -> log.info("删除 key:{}, value:{}", key, value))
            .build();

    private final LoadingCache<String, String> loadingCache = Caffeine.newBuilder()
            .expireAfterWrite(1,TimeUnit.MINUTES)
            .maximumSize(10_000)
            .build(key -> "a");

    private final LoadingCache<String, Integer> dynamicLoadingCache = Caffeine.newBuilder()
            .expireAfterWrite(1,TimeUnit.MINUTES)
            // refreshAfterWrite这个参数是 LoadingCache 和 AsyncLoadingCache 的才能用。
            // 在写操作之后的一段时间后允许 key 对应的缓存元素进行刷新，但是被这个 key 查询到的时候才会正式进行刷新操作。
            // 在刷新的时候如果查询缓存元素，其旧值将仍被返回，直到该元素的刷新完毕后结束后才会返回刷新后的新值。
            .refreshAfterWrite(5, TimeUnit.SECONDS)
            .maximumSize(10)
            // 当key不存在，则执行业务功能获取数据，并设置缓存value
            .build(key -> atomicInteger.incrementAndGet());

    /**
     * 每读缓存key一次，缓存过期时间延长
     */
    private final LoadingCache<String, Integer> dynamicReadLoadingCache = Caffeine.newBuilder()
            // 每次访问key都延迟过期时间
            .expireAfterAccess(10, TimeUnit.SECONDS)
            .refreshAfterWrite(1, TimeUnit.MINUTES)
            .maximumSize(10)
            // 当key不存在，则执行业务功能获取数据，并设置缓存value
            .build(key -> atomicInteger.incrementAndGet());

    private final AsyncLoadingCache<String, String> asyncLoadingCache = Caffeine.newBuilder()
            .expireAfterWrite(1,TimeUnit.MINUTES)
            .maximumSize(10_000)
            .refreshAfterWrite(5, TimeUnit.SECONDS)
            .buildAsync(key -> {
                log.info("异步加载缓存");
                Thread.sleep(1000);
                return "a";
            });

    public String getCacheValue(String key) {
       return cache.getIfPresent(key);
    }

    public void setCacheValue(String key, String value) {
        cache.put(key, value);
    }

    public void invalidateCache(String key) {
        cache.invalidate(key);
    }

    public String getByDefault(String key) {
        return cache.get(key, k -> "a");
    }

    public String getLoadingCacheByDefault(String key) {
        return loadingCache.get(key);
    }

    /**
     * 异步加载缓存，当value不存，进行异步执行业务代码，获取的值放入缓存
     * @param key 缓存key
     * @return 返回缓存value
     */
    @SneakyThrows
    public String getAsyncLoadingCacheByDefault(String key) {
        CompletableFuture<String> future = asyncLoadingCache.get(key);
        log.info("获取CompletableFuture对象");
        return future.get();
    }

    public Integer getDynamicLoadingCacheValue(String key) {
        return dynamicLoadingCache.get(key);
    }

    public Integer getDynamicReadLoadingCache(String key) {
        return dynamicReadLoadingCache.get(key);
    }
}
